﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace HobSoft.SoccerBet2014.Common.Json
{
    public static class JsonExtensions
    {
        static Dictionary<Type, Func<string, bool>> CheckCanCast = new Dictionary<Type, Func<string, bool>>()
        {
            {typeof(short), v => { short val; return short.TryParse(v, out val); }},
            {typeof(ushort), v => { ushort val; return ushort.TryParse(v, out val); }},
            {typeof(int), v => { int val; return int.TryParse(v, out val); }},
            {typeof(uint), v => { uint val; return uint.TryParse(v, out val); }},
            {typeof(long), v => { long val; return long.TryParse(v, out val); }},
            {typeof(ulong), v => { ulong val; return ulong.TryParse(v, out val); }},
            {typeof(decimal), v => { decimal val; return decimal.TryParse(v, out val); }},
            {typeof(float), v => { float val; return float.TryParse(v, out val); }},
            {typeof(double), v => { double val; return double.TryParse(v, out val); }},
            {typeof(sbyte), v => { sbyte val; return sbyte.TryParse(v, out val); }},
            {typeof(byte), v => { byte val; return byte.TryParse(v, out val); }},
            {typeof(bool), v => { bool val; return bool.TryParse(v, out val); }},
            {typeof(char), v => { char val; return char.TryParse(v, out val); }},
            {typeof(DateTime), v => { DateTime val; return DateTime.TryParse(v, out val); }}
        };

        static Dictionary<Type, Func<string, object, object>> TryCast = new Dictionary<Type, Func<string, object, object>>()
        {
            {
                typeof(short), 
                (v, d) => { 
                    short val;  
                    if(short.TryParse(v, out val))
                        return val;
                    return d;
                }
            },
            {
                typeof(ushort), 
                (v, d) => {
                    ushort val;
                    if (ushort.TryParse(v, out val))
                        return val;
                    return d;
                }
            },
            {
                typeof(int), 
                (v, d) => {
                    int val;
                    if (int.TryParse(v, out val))
                        return val;
                    return d;
                }
            },
            {
                typeof(uint), 
                (v, d) => {
                    uint val;
                    if (uint.TryParse(v, out val))
                        return val;
                    return d;
                }
            },
            {
                typeof(long), 
                (v, d) => {
                    long val;
                    if (long.TryParse(v, out val))
                        return val;
                    return d;
                }
            },
            {
                typeof(ulong), 
                (v, d) => {
                    ulong val;
                    if (ulong.TryParse(v, out val))
                        return val;
                    return d;
                }
            },
            {
                typeof(decimal), 
                (v, d) => {
                    decimal val;
                    if (decimal.TryParse(v, out val))
                        return val;
                    return d;
                }
            },
            {
                typeof(float), 
                (v, d) => {
                    float val;
                    if (float.TryParse(v, out val))
                        return val;
                    return d;
                }
            },
            {
                typeof(double), 
                (v, d) => {
                    double val;
                    if (double.TryParse(v, out val))
                        return val;
                    return d;
                }
            },
            {
                typeof(sbyte), 
                (v, d) => {
                    sbyte val;
                    if (sbyte.TryParse(v, out val))
                        return val;
                    return d;
                }
            },
            {
                typeof(byte), 
                (v, d) => {
                    byte val;
                    if (byte.TryParse(v, out val))
                        return val;
                    return d;
                }
            },
            {
                typeof(bool), 
                (v, d) => {
                    bool val;
                    if (bool.TryParse(v, out val))
                        return val;
                    return d;
                }
            },
            {
                typeof(char), 
                (v, d) => {
                    char val;
                    if (char.TryParse(v, out val))
                        return val;
                    return d;
                }
            },
            {
                typeof(DateTime), 
                (v, d) => {
                    DateTime val;
                    if (DateTime.TryParse(v, out val))
                        return val;
                    return d;
                }
            }
        };

        public static bool CanCast<T>(this JToken jt) where T : struct
        {
            if (!jt.CanString()) return false;

            var val = jt.Value<string>();
            var tp = typeof(T);
            if (CheckCanCast.ContainsKey(tp))
                return CheckCanCast[tp](val);
            return false;
        }

        public static bool CanString(this JToken jt)
        {
            if (jt.Type == JTokenType.Null
                || jt.Type == JTokenType.None
                || jt.Type == JTokenType.Undefined
                || jt.Type == JTokenType.Array
                || jt.Type == JTokenType.Object
                || jt.Type == JTokenType.Constructor)
                return false;
            return true;
        }

        public static T TryCaseValue<T>(this JToken jt, T def) where T : struct
        {
            if (!jt.CanString()) return def;

            var val = jt.Value<string>();
            var tp = typeof(T);
            if (TryCast.ContainsKey(tp))
                return (T)TryCast[tp](val, def);
            return def;
        }
    }
}
